﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Sublight.Plugins.SubtitleProvider;
using Sublight.Plugins.SubtitleProvider.Types;

namespace SublightPlugin
{
    public class Subscene : BaseSubtitleProvider, ISubtitleProvider
    {
        private byte[] DownloadUtility(string id)
        {
            string url = _subtitleUrl[id];

            var hwr = WebRequest.Create(url) as HttpWebRequest;
            if (hwr == null) return null;

            hwr.Method = "GET";
            var response = hwr.GetResponse();

            Stream stream = response.GetResponseStream();
            var ms = new MemoryStream();
            while (true)
            {
                var buffer = new byte[10240];
                int len = stream.Read(buffer, 0, buffer.Length);
                if (len <= 0) break;
                ms.Write(buffer, 0, len);
            }

            string htmlData = Encoding.UTF8.GetString(ms.ToArray());
            Match m = Regex.Match(htmlData, @"<input.*?type=""hidden"".*?__VIEWSTATE.*?value=""(?<vs>.*?)"" />", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            if (!m.Success) return null;
            string vs = m.Groups["vs"].Value;

            hwr = WebRequest.Create(url) as HttpWebRequest;
            if (hwr == null) return null;

            hwr.Method = "POST";

            hwr.Referer = url;
            hwr.ContentType = "application/x-www-form-urlencoded;";
            hwr.KeepAlive = true;
            hwr.Headers.Add("Accept-Encoding", "gzip, deflate");

            var sbReq = new StringBuilder();
            sbReq.AppendFormat("__VIEWSTATE={0}&", vs);
            sbReq.AppendFormat("subtitleId={0}&", id);
            sbReq.AppendFormat("typeId={0}", _subtitleType[id]);

            byte[] reqBuffer = Encoding.UTF8.GetBytes(sbReq.ToString());
            hwr.ContentLength = reqBuffer.Length;
            Stream rs = hwr.GetRequestStream();
            rs.Write(reqBuffer, 0, reqBuffer.Length);

            response = hwr.GetResponse();
            stream = response.GetResponseStream();

            ms = new MemoryStream();
            while (true)
            {
                try
                {
                    var buffer = new byte[10240];
                    int len = stream.Read(buffer, 0, buffer.Length);
                    if (len <= 0) break;
                    if (!stream.CanRead) break;
                    ms.Write(buffer, 0, len);
                }
                catch (IOException)
                {
                    break;
                }
            }

            byte[] data = ms.ToArray();

            return data;
        }

        #region ISubtitleProvider Members

        public SubtitleBasicInfo[] Search(string title, SearchFilter[] filter, out string error)
        {
            error = null;
            return DoSearch(title, filter);
        }

        public byte[] DownloadById(string id)
        {
            if (_subtitleType == null || _subtitleUrl == null) return null;

            string subtitleType = _subtitleType[id];

            if (subtitleType == "zip")
            {
                return DownloadUtility(id);
            }
            if (subtitleType == "rar")
            {
                return Compression.Utility.RarToZip(DownloadUtility(id));
            }
            return null;
        }

        public string GetDownloadUrl(string id)
        {
            return null;
        }

        public SubtitleProviderDownloadType DownloadType
        {
            get { return SubtitleProviderDownloadType.Direct; }
        }

        public SubtitleAction[] GetActions(string id)
        {
            return null;
        }

        #endregion

        #region ISubtitleProviderInfo Members

        public System.Drawing.Image Logo
        {
            get { return null; }
        }

        public string ShortName
        {
            get { return "subscene.com"; }
        }

        public string Info
        {
            get
            {
                return "subscene.com subtitle provider";
            }
        }

        public Version Version
        {
            get
            {
                return new Version(1, 0, 0);
            }
        }

        #endregion

        #region Properties
        #region LanguageMapper
        protected override LanguageMapperUtility LanguageMapper
        {
            get
            {
                if (_languageMapper == null)
                {
                    _languageMapper = new LanguageMapperUtility();
                    _languageMapper.Map(Language.English, "English");
                    _languageMapper.Map(Language.Arabic, "Arabic");
                    _languageMapper.Map(Language.Croatian, "Croatian");
                    _languageMapper.Map(Language.PortugueseBrazil, "Brazillian Portuguese");
                    _languageMapper.Map(Language.Bulgarian, "Bulgarian");
                    _languageMapper.Map(Language.Danish, "Danish");
                    _languageMapper.Map(Language.Dutch, "Dutch");
                    _languageMapper.Map(Language.Persian, "Farsi/Persian");
                    _languageMapper.Map(Language.Finnish, "Finnish");
                    _languageMapper.Map(Language.French, "French");
                    _languageMapper.Map(Language.Greek, "Greek");
                    _languageMapper.Map(Language.Hebrew, "Hebrew");
                    _languageMapper.Map(Language.Hungarian, "Hungarian");
                    _languageMapper.Map(Language.Korean, "Korean");
                    _languageMapper.Map(Language.Macedonian, "Macedonian");
                    _languageMapper.Map(Language.Norwegian, "Norwegian");
                    _languageMapper.Map(Language.Portuguese, "Portuguese");
                    _languageMapper.Map(Language.Romanian, "Romanian");
                    _languageMapper.Map(Language.SerbianLatin, "Serbian");
                    _languageMapper.Map(Language.Spanish, "Spanish");
                    _languageMapper.Map(Language.Swedish, "Swedish");
                    _languageMapper.Map(Language.Turkish, "Turkish");
                    _languageMapper.Map(Language.Vietnamese, "Vietnamese");
                    _languageMapper.Map(Language.Albanian, "Albanian");
                    _languageMapper.Map(Language.Czech, "Czech");
                    _languageMapper.Map(Language.Icelandic, "Icelandic");
                    _languageMapper.Map(Language.Indonesian, "Indonesian");
                    _languageMapper.Map(Language.Italian, "Italian");
                    _languageMapper.Map(Language.Lithuanian, "Lithuanian");
                    _languageMapper.Map(Language.Polish, "Polish");
                    _languageMapper.Map(Language.Slovak, "Slovak");
                    _languageMapper.Map(Language.Slovenian, "Slovenian");
                }
                return _languageMapper;
            }
        }
        private LanguageMapperUtility _languageMapper;
        #endregion

        #region WebClient
        protected WebClient WebClient
        {
            get
            {
                if (_webClient == null)
                {
                    _webClient = new WebClient();

                    var client = new WebClient();
                    client.Headers.Add("User-Agent", UserAgent);
                    client.Headers.Add("Connection", "Keep-Alive");
                    client.Headers.Add("Accept-Encoding", "gzip, deflate");

                    _webClient.Encoding = Encoding.UTF8;
                }

                return _webClient;
            }
        }

        private WebClient _webClient;
        #endregion
        #endregion //Properties

        #region Methods
        private SubtitleBasicInfo[] DoSearch(string title, SearchFilter[] filter)
        {
            _subtitleType = new Dictionary<string, string>();
            _subtitleUrl = new Dictionary<string, string>();

            string[] urls = ReturnSubtitleListUrl(title);

            var lstRes = new List<SubtitleInfo>();

            foreach (string url in urls)
            {
                string htmlResponse = WebClient.DownloadString(url);
                MatchCollection mc = Regex.Matches(htmlResponse, @"<tr>.*?<td>.*?<a class=""a1"".*?javascript:Subtitle[(](?<id>.*?), '(?<type>.*?)'.*?[)].*?<span.*?>(?<lang>.*?)</span>.*?<span.*?>(?<title>.*?)</span>.*?<td class=""a3"">(?<discs>[0-9]+?).*?<a.*?Profile.aspx.*?>(?<username>.*?)</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

                var matchesImdb = Regex.Matches(htmlResponse, @"<a href=""http://imdb.com/title/(?<imdb>.*?)"">");
                string imdb = null;
                if (matchesImdb.Count == 1)
                {
                    if (matchesImdb[0].Success)
                    {
                        imdb = matchesImdb[0].Groups["imdb"].Value.Trim();
                    }
                }

                bool stopSearch = false;
                foreach (Match m in mc)
                {
                    if (MaxResultsReached(filter, lstRes.Count))
                    {
                        stopSearch = true;
                        break;
                    }

                    string id;
                    string type;
                    string lang;
                    string subTitle;
                    string username;

                    try
                    {
                        id = m.Groups["id"].Value.Trim();
                        type = m.Groups["type"].Value.Trim().ToLower();
                        lang = m.Groups["lang"].Value.Trim();
                        subTitle = m.Groups["title"].Value.Trim();
                        username = m.Groups["username"].Value.Trim();
                    }
                    catch
                    {
                        continue;
                    }

                    byte discs = 0;
                    try
                    {
                        if (!byte.TryParse(m.Groups["discs"].Value.Trim(), out discs))
                        {
                            discs = 0;
                        }
                    }
                    catch
                    {
                    }

                    if (string.Compare(username, "empty", true) == 0) username = null;

                    var si = new SubtitleInfo
                    {
                        Id = id,
                        Language = LanguageIdToLanguage(lang),
                        Title = subTitle,
                        Publisher = username,
                    };
                    if (discs > 0)
                    {
                        si.MediaCount = discs;
                        si.MediaFormat = MediaFormat.CD;
                    }
                    if (!string.IsNullOrEmpty(imdb))
                    {
                        si.Imdb = string.Format("http://www.imdb.com/title/{0}", imdb);
                    }

                    if (!si.AreMandatoryFieldsSet)
                    {
                        continue;
                    }

                    if (!DoesMatchFilter(si, filter))
                    {
                        continue;
                    }

                    if (!_subtitleType.ContainsKey(id))
                    {
                        _subtitleType.Add(id, type);
                    }

                    if (!_subtitleUrl.ContainsKey(id))
                    {
                        _subtitleUrl.Add(id, url);
                    }

                    lstRes.Add(si);
                }

                if (stopSearch)
                {
                    break;
                }
            }

            return lstRes.ToArray();
        }

        private Language LanguageIdToLanguage(string id)
        {
            try
            {
                return LanguageMapper.GetLanguage(id);
            }
            catch
            {
                return Language.Unknown;
            }
        }

        private static string[] ReturnSubtitleListUrl(string title)
        {
            var hwr = WebRequest.Create(string.Format("http://subscene.com/filmsearch.aspx?q={0}&exact=on", HttpUtility.UrlEncode(title))) as HttpWebRequest;
            if (hwr == null) return null;

            hwr.Method = "GET";
            var res = hwr.GetResponse();

            Stream stream = res.GetResponseStream();
            var ms = new MemoryStream();
            while (true)
            {
                var buffer = new byte[10240];
                int len = stream.Read(buffer, 0, buffer.Length);
                if (len <= 0) break;
                ms.Write(buffer, 0, len);
            }

            string response = Encoding.UTF8.GetString(ms.ToArray());

            var urls = new List<string>();

            Match m = Regex.Match(response, @"<div id=""filmSearch"">(?<links>.*?)</div>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

            if (m.Success)
            {
                string links = m.Groups["links"].Value;

                var matches = Regex.Matches(links, @"<a href=""(?<link>.*?)"".*?</a>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

                foreach (Match match in matches)
                {
                    if (match.Success)
                    {
                        string url = match.Groups["link"].Value;
                        url = string.Format("http://subscene.com/{0}", url.Trim(new[] { ' ', '/' }));
                        urls.Add(url);
                    }
                }

            }
            else
            {
                string url = res.ResponseUri.ToString();
                if (!string.IsNullOrEmpty(url))
                {
                    urls.Add(url);
                }
            }

            return urls.ToArray();
        }

        #endregion //Methods

        #region Fields
        private Dictionary<string, string> _subtitleType;
        private Dictionary<string, string> _subtitleUrl;
        #endregion
    }
}
